package ink.tiptop.utils.ssh;

import com.trilead.ssh2.Connection;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 2020年3月24日18:43:37
 * zjf
 * ssh连接池
 */
@Slf4j
public class SshPool {

    @Getter
    private final SshInfo sshInfo;
    private Queue<Connection> queue;
    private List<Connection> used;

    public SshPool(SshInfo sshInfo, int size) throws Exception {
        if (sshInfo == null || size <= 0) {
            throw new RuntimeException("初始化不可空");
        }
        queue = new ArrayBlockingQueue<>(size);
        used = new ArrayList<>(size);
        this.sshInfo = sshInfo;
        initConnections(sshInfo, size);
    }

    private void initConnections(SshInfo sshInfo, int size) throws Exception {
        log.info("初始化connection中...");
        SshInfo remote;
        int add = 0;
        while (true) {
            add++;
            if (add >= size * 5) {
                log.error("尝试初始化ssh连接池,尝试次数过多,可能是配置有误");
                break;
            }
            if (queue.size() >= size) {
                break;
            }
            remote = new SshInfo(
                    sshInfo.getHost(),
                    sshInfo.getPort(),
                    sshInfo.getUsername(),
                    sshInfo.getPassword(),

                    sshInfo.getIdentity(),
                    sshInfo.getPassphrase()
            );
            try {
                Connection connection = SshUtil.connect(remote);
                tryAdd(connection);
            } catch (Exception e) {
                throw e;
            }
        }
        log.info("初始化connection成功,size:{}", queue.size());
    }

    /**
     * 尝试添加到连接队列中
     *
     * @param conn
     */
    private void tryAdd(Connection conn) throws Exception {
        List<String> pwd = SshUtil.refreshConnection(conn, "pwd");
        if (!pwd.isEmpty()) {
            // 添加活动的session
            queue.add(conn);
            return;
        }
        throw new RuntimeException("session不可用");
    }

    public Connection get() {
        // 读操作 如果是多个poolid相同改动话...不可能出现这个情况,所以不需要加锁
        // 这个加锁是为了和刷新conn区分开来操作 所以这个id是fresh_id
        Connection poll = null;
        while (true) {
            try {
                poll = queue.poll();
                used.add(poll);
                if (queue.size() <= 0) {
                    queue.addAll(used);
                    used.clear();
                }
                if (poll == null) {
                    throw new NullPointerException("获取到的conn为null");
                }
                return poll;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void clear() {
        queue.clear();
        used.clear();
    }

}
